package com.pai4j.message.core.sender;

import com.pai4j.message.model.MessageContext;
import com.pai4j.message.model.SendResult;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 消息发送器抽象基类
 * 实现通用的发送逻辑，子类只需实现特定的发送逻辑
 *
 * @author PAI4J Team
 * @date 2025-11-01
 */
@Slf4j
public abstract class AbstractMessageSender implements MessageSender {

    /**
     * 最大重试次数
     */
    protected int maxRetries = 3;

    /**
     * 重试间隔（毫秒）
     */
    protected long retryInterval = 1000L;

    @Override
    public SendResult send(MessageContext context) {
        if (context == null) {
            return SendResult.failure("INVALID_CONTEXT", "消息上下文不能为空", getChannel());
        }

        long startTime = System.currentTimeMillis();

        try {
            // 前置处理
            preHandle(context);

            // 执行发送（带重试）
            SendResult result = sendWithRetry(context);

            // 设置耗时
            long costTime = System.currentTimeMillis() - startTime;
            result.setCostTime(costTime);
            result.setChannel(getChannel());

            // 后置处理
            postHandle(context, result);

            return result;

        } catch (Exception e) {
            log.error("消息发送异常: channel={}, error={}", getChannel(), e.getMessage(), e);
            
            // 异常处理
            handleException(context, e);
            
            return SendResult.failure("SEND_ERROR", e.getMessage(), getChannel())
                    .addExtData("exception", e.getClass().getName());
        }
    }

    /**
     * 带重试的发送
     */
    private SendResult sendWithRetry(MessageContext context) {
        int attempts = 0;
        Exception lastException = null;

        while (attempts < maxRetries) {
            attempts++;
            
            try {
                SendResult result = doSend(context);
                
                if (result.isSuccess()) {
                    if (attempts > 1) {
                        log.info("消息发送成功（重试{}次后成功）: channel={}", attempts - 1, getChannel());
                    }
                    return result;
                }
                
                // 如果不是可重试的错误，直接返回
                if (!isRetryable(result)) {
                    log.warn("消息发送失败（不可重试）: code={}, message={}", 
                            result.getCode(), result.getMessage());
                    return result;
                }
                
                log.warn("消息发送失败，准备重试: attempt={}/{}, code={}", 
                        attempts, maxRetries, result.getCode());
                
            } catch (Exception e) {
                lastException = e;
                log.warn("消息发送异常，准备重试: attempt={}/{}, error={}", 
                        attempts, maxRetries, e.getMessage());
            }

            // 最后一次尝试失败
            if (attempts >= maxRetries) {
                break;
            }

            // 重试前等待
            try {
                TimeUnit.MILLISECONDS.sleep(retryInterval * attempts); // 指数退避
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                break;
            }
        }

        // 所有重试都失败
        String errorMsg = lastException != null ? lastException.getMessage() : "发送失败";
        return SendResult.failure("MAX_RETRIES_EXCEEDED", 
                String.format("重试%d次后仍然失败: %s", maxRetries, errorMsg), 
                getChannel());
    }

    /**
     * 判断错误是否可重试
     */
    protected boolean isRetryable(SendResult result) {
        if (result == null || result.isSuccess()) {
            return false;
        }
        
        // 可重试的错误码
        String code = result.getCode();
        return "SEND_ERROR".equals(code) || 
               "TIMEOUT".equals(code) || 
               "NETWORK_ERROR".equals(code);
    }

    @Override
    public List<SendResult> batchSend(List<MessageContext> contexts) {
        List<SendResult> results = new ArrayList<>();
        
        if (contexts == null || contexts.isEmpty()) {
            return results;
        }

        // 默认实现：逐个发送
        for (MessageContext context : contexts) {
            results.add(send(context));
        }

        return results;
    }

    @Override
    public CompletableFuture<SendResult> sendAsync(MessageContext context) {
        return CompletableFuture.supplyAsync(() -> send(context));
    }

    /**
     * 执行发送（子类实现）
     */
    protected abstract SendResult doSend(MessageContext context);

    /**
     * 前置处理
     */
    protected void preHandle(MessageContext context) {
        // 默认不做处理
        log.debug("消息发送前置处理: channel={}, senderId={}, receiverId={}", 
                getChannel(), context.getSenderId(), context.getReceiverId());
    }

    /**
     * 后置处理
     */
    protected void postHandle(MessageContext context, SendResult result) {
        // 默认不做处理
        log.debug("消息发送后置处理: channel={}, success={}, costTime={}ms", 
                getChannel(), result.isSuccess(), result.getCostTime());
    }

    /**
     * 异常处理
     */
    protected void handleException(MessageContext context, Exception e) {
        // 默认只记录日志
        log.error("消息发送异常处理: channel={}, error={}", getChannel(), e.getMessage());
    }

    /**
     * 生成消息ID
     */
    protected String generateMessageId() {
        return String.valueOf(System.currentTimeMillis()) + "-" + 
               String.valueOf((int)(Math.random() * 10000));
    }

    /**
     * 验证上下文
     */
    protected boolean validateContext(MessageContext context) {
        if (context == null) {
            return false;
        }
        if (context.getSenderId() == null || context.getSenderId().trim().isEmpty()) {
            log.warn("发送者ID不能为空");
            return false;
        }
        if (context.getReceiverId() == null || context.getReceiverId().trim().isEmpty()) {
            log.warn("接收者ID不能为空");
            return false;
        }
        if (context.getMessageDTO() == null) {
            log.warn("消息DTO不能为空");
            return false;
        }
        return true;
    }
}

